package com.aizuda.encrypt.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.aizuda.encrypt.config.EncryptProperties;
import com.aizuda.encrypt.entity.EncryptInfo;
import com.aizuda.encrypt.entity.EncryptRule;
import com.aizuda.encrypt.entity.EncryptedValuesParams;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.util.List;

/**
 * 加密参数重写器
 *
 * @author nn200433
 * @date 2024-03-25 11:16:06
 */
@Slf4j
public class EncryptPreParameterRewriter {

    private final EncryptRule rule;

    public EncryptPreParameterRewriter(EncryptProperties properties) {
        rule = new EncryptRule(properties);
    }

    /**
     * 重写
     *
     * @param sql               sql语句
     * @param sqlType           sql类型
     * @param parameterMappings 参数映射
     * @param parameterObject   参数对象
     * @throws JSQLParserException JSQLParserException
     */
    public void rewrite(String sql, SqlCommandType sqlType, List<ParameterMapping> parameterMappings, Object parameterObject)
            throws JSQLParserException {
        final JSQLStatementContext context      = new JSQLStatementContext(sql, rule);
        final List<EncryptInfo>    encryptInfos = context.createEncryptInfos();
        if (encryptInfos.isEmpty()) {
            return;
        }
        for (EncryptInfo each : encryptInfos) {
            encryptParameters(each, parameterMappings, parameterObject, sqlType);
        }
    }

    /**
     * 加密参数
     *
     * @param encryptInfo       加密信息
     * @param parameterMappings 参数映射
     * @param parameterObject   参数对象
     */
    private void encryptParameters(EncryptInfo encryptInfo, List<ParameterMapping> parameterMappings,
                                   Object parameterObject, SqlCommandType sqlType) {
        final int index = encryptInfo.getIndex();
        if (CollUtil.isNotEmpty(parameterMappings) && null != parameterObject) {
            final ParameterMapping parameterMapping = parameterMappings.get(index - 1);
            final String           property         = parameterMapping.getProperty();
            final MetaObject       metaObject       = SystemMetaObject.forObject(parameterObject);
            final Object           value            = metaObject.getValue(property);
            final String           originalValues   = Convert.toStr(value);
            final String           tableName        = encryptInfo.getTableName();
            final String           columnName       = encryptInfo.getColumnName();
            EncryptedValuesParams  params           = new EncryptedValuesParams();
            params.setIsAddIndex(Boolean.TRUE);
            params.setSqlType(sqlType);
            final Object encryptedValues = rule.getEncryptedValues(tableName, columnName, originalValues, params);
            log.debug("---> [Mybatis SQL 加密拦截] 表名 = {} ， 字段 = {} ， 加密前 = {} ， 加密后 = {}", tableName, columnName, originalValues, encryptedValues);
            metaObject.setValue(property, encryptedValues);
        }
    }

}
